﻿using System;
using System.Diagnostics;
using System.Reflection;

namespace Reflective
{
    /// <summary>
    /// Extension methods for <see cref="MethodInfo"/>.
    /// </summary>
    public static class MethodInfoExtensions
    {
        /// <summary>
        /// Gets the corresponding <see cref="MethodInfo"/> object for
        /// the method in a class that implements a specific method
        /// from an interface.
        /// </summary>
        /// <param name="interfaceMethod">
        /// The <see cref="MethodInfo"/> for the method to locate the
        /// implementation of.</param>
        /// <param name="classType">
        /// The <see cref="Type"/> of the class to find the implementing
        /// method for.
        /// </param>
        /// <returns>
        /// The <see cref="MethodInfo"/> of the method that implements
        /// <paramref name="interfaceMethod"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// <para><paramref name="interfaceMethod"/> is <c>null</c>.</para>
        /// <para>- or -</para>
        /// <para><paramref name="classType"/> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="ArgumentException">
        /// <para><paramref name="interfaceMethod"/> is not defined in an interface.</para>
        /// </exception>
        public static MethodInfo GetImplementingMethod(this MethodInfo interfaceMethod, Type classType)
        {
            if (interfaceMethod == null)
                throw new ArgumentNullException("interfaceMethod");
            if (classType == null)
                throw new ArgumentNullException("classType");
            if (!interfaceMethod.DeclaringType.IsInterface)
                throw new ArgumentException("interfaceMethod is not defined by an interface", "interfaceMethod");
            if (!interfaceMethod.DeclaringType.IsAssignableFrom(classType))
                throw new ArgumentException("classType does not implement the interface the method is declared in", "classType");

            InterfaceMapping map = classType.GetInterfaceMap(interfaceMethod.DeclaringType);
            MethodInfo result = null;

            for (int index = 0; index < map.InterfaceMethods.Length; index++)
            {
                if (map.InterfaceMethods[index] == interfaceMethod)
                    result = map.TargetMethods[index];
            }

            Debug.Assert(result != null, "Unable to locate MethodInfo for implementing method");

            return result;
        }
    }
}